﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using MoqSamples.Models;

namespace Moq_QuickStart.Test
{
    [TestClass]
    public class HelloWorld_Moq
    {
        [TestMethod]
        public void Create_A_Mock_Object_With_Mock_Data_And_Test_If_It_Works()
        {
            //The first line of code creates the mock from the IProduct interface. 
            //The Mock class is a class from the Moq framework. 
            //The Mock class has a generic constructor that accepts the type of 
            //the interface to create.
            var newProduct = new Mock<IProduct>();
            //the Id property is setup to return the value 1 
            
            //Notice how lambda expressions are used to represent the Id and Name property.
            //The advantage of using a lambda expression to represent a property
            //instead of a string is that refactoring tools such as Resharper can 
            //refactor the properties automatically.
            //[setting-up expectation]
            newProduct
                .ExpectGet(p => p.Id)
                .Returns(1);
            //the Name property is setup to return the value “Bushmills"
            newProduct
                .ExpectGet(p => p.Name)
                .Returns("Bushmills:Hello World");

            //After you create the mock newProduct, you can use the mock just as if you had 
            //actually implemented the IProduct interface. For example, 
            //the following Assert executes successfully:

            /*
             * Notice that you must use newProduct.Object when referring to the mock newProduct. 
             * The newProduct variable represents the proxy class and 
             * the newProduct.Object variable represents the actual newProduct class
            */

            Assert.AreEqual("Bushmills:Hello World", newProduct.Object.Name);

        }

        [TestMethod]
        public void Using_Mock_In_Method_Call_With_Fixed_Return_Value_1()
        {
            var newProduct = new Mock<IProduct>();

            newProduct
                .ExpectGet(p => p.Id)
                .Returns(1);
            newProduct
                .ExpectGet(p => p.Name)
                .Returns("Bushmills:Hello World");

            // Mock product repository (set it up!)
            var productRepository = new Mock<IProductRepository>();
            productRepository
                .Expect(p => p.Get(1))
                .Returns(newProduct.Object);

            // Mock product repository (use it)
            var productReturned = productRepository.Object.Get(1);
            Assert.AreEqual("Bushmills:Hello World", productReturned.Name);
            
        }

        [TestMethod]
        public void Using_Mock_In_Method_Call_With_Any_Int_Return_Value()
        {
            var newProduct = new Mock<IProduct>();

            newProduct
                .ExpectGet(p => p.Id)
                .Returns(1);
            newProduct
                .ExpectGet(p => p.Name)
                .Returns("Bushmills:Hello World");

            // Mock product repository (set it up!)
            var productRepository = new Mock<IProductRepository>();
            productRepository
                .Expect(p => p.Get(It.IsAny<int>()))
                .Returns(newProduct.Object);

            // Mock product repository (use it)
            var productReturned = productRepository.Object.Get(2);
            Assert.AreEqual("Bushmills:Hello World", productReturned.Name);
        }

        [TestMethod]
        public void Using_Mock_In_Method_Call_With_Any_Int_Return_Value_In_A_Range()
        {
            var newProduct = new Mock<IProduct>();

            //setting up the mock
            newProduct
                .ExpectGet(p => p.Id)
                .Returns(1);
            newProduct
                .ExpectGet(p => p.Name)
                .Returns("Bushmills:Hello World");

            // Mock product repository (set it up!)
            var productRepository = new Mock<IProductRepository>();
            productRepository
                .Expect(p => p.Get(It.Is<int>(id => id > 0 && id < 6)))
                .Returns(newProduct.Object);

            // Mock product repository (use it)
            var productReturned = productRepository.Object.Get(2);
            Assert.AreEqual("Bushmills:Hello World", productReturned.Name);
        }

        [TestMethod]
        public void Using_Property_Verify()
        {
            var newProduct = new Mock<IProduct>();

            //When the Verify() method is called at the end of the test, 
            //if any of the expectations marked as verifiable have not been called, 
            //then an exception is thrown.
            newProduct
               .ExpectGet(p => p.Id)
               .Returns(1)
               .Verifiable();

            //now call the Property - Get accssesor, 
            //which will cause the newProduct.Verify() passed
            int i = newProduct.Object.Id;

            newProduct.Verify();
            
        }

        [TestMethod]
        public void Using_Method_Verify()
        {
            var newProduct = new Mock<IProduct>();

            var productRepository = new Mock<IProductRepository>();
            productRepository
                .Expect(p => p.Get(1))
                .Returns(newProduct.Object)
                .Verifiable();

            //now call the method, 
            //which will cause the newProduct.Verify() passed
            var productReturned = productRepository.Object.Get(1);

            productRepository.Verify();
        }
    }
}
